home *** CD-ROM | disk | FTP | other *** search
Text File | 1988-08-16 | 12.6 KB | 353 lines | [TEXT/MPS ] |
- {------------------------------------------------------------------------------
- #
- # Apple Macintosh Developer Technical Support
- #
- # Pop-up Menu Example Application
- #
- # PopMenus
- #
- # PopMenus.p - Pascal Source
- #
- # Copyright © 1988 Apple Computer, Inc.
- # All rights reserved.
- #
- # Versions: 1.0 8/88
- #
- # Components: PopMenus.p August 1, 1988
- # PopMenus.r August 1, 1988
- # PopMenus.make August 1, 1988
- #
- # This program is a simple example of how to use pop-up menus in your
- # application. It implements a pop-up as a userItem in a modal dialog
- # box (this is a helpful example in its own right!).
- #
- # See Sample and TESample for the general structure and MultiFinder
- # techniques that we recommend that you use when building a new application.
- #
-
- A few tips:
-
- The Menu Manager provides just one routine, PopUpMenuSelect, that you
- call when the user clicks on the current selection of your pop-up. Your
- application is responsible for drawing the current selection (and the
- title that goes with it), and hilighting the title before calling
- PopUpMenuSelect, and unhilighting afterwards.
-
- The metrics involved with drawing the current selection are a little
- tricky; this example does things in the recommended way. Note the
- following as you try the example (it helps to have a long font name
- to see some of the extreme cases!):
-
- - There is one pixel between the title's boundsrect (as defined in the
- DITL, a staticText item) and the popup's (a userItem). Also, the
- userItem's boundsrect (as defined) is used as the area INSIDE the
- current selection box. This is the easiest way to handle this; it's
- inconsistent in that normally, clicking on the drop shadow would get
- you what you want (in this case, these clicks are ignored; the user
- must click within the box). The only case where this is a problem
- is if you filter the drawing of items in response to an update event
- to those items that lie within the update region (if just the drop
- shadow needed updating, it wouldn't be updated with this
- implementation).
-
- - The current selection is always drawn in the same place & same
- size. To prevent the selection from drawing wider than the box,
- the selection is truncated and ellipses added (if necessary). This
- algorithm may be handy elsewhere, too! This particular example has
- an especially narrow current selection box (to show off the truncation
- feature); you'll probably want to make yours wide enough to accomodate
- most possible values.
-
- - The title is highlighted while the pop-up is up (that is, during
- PopUpMenuSelect), and presumably, during the duration of any operation
- generated by the chosen item. This is intended to work just like the
- hilighting of a regular menu title.
-
- [You need to have more than about seven font families in your system
- file to see this one:] A "feature" of the Menu Manager is that it must
- call the menu defproc just once to allocate the space beneath the menu.
- Also, the menu must be kept onscreen (scrolling if necessary). Also,
- the human interface dictates that the currently-chosen item should
- appear under the mouse when the pop-up is presented. These three
- requirements conflict; note the case where the last item is the current
- selection.
-
- The menu manager will be happy to put the current item whereever you
- say it should, but may leave white space in the menu if it's necessary
- to "pre-scroll" the menu to give the selection you asked for. This
- white space appears because the bits beneath the menu are saved and
- restored just once (each).
-
- The upshot of all this is that this is a Menu Manager gotcha; there is
- no workaround, but the problem may be fixed in future Systems.
-
- Pop-up menus must be InsertMenu'd just like hierarchical submenus; they
- should left in the menu list only while PopUpMenuSelect is being called.
- The example handles this correctly.
-
- Applications' pop-up menus' IDs, should always be in the range 1 through
- 235 (inclusive); desk accessories' pop-ups should use IDs 236 through
- 255. This really applies to all menus; it's most important for pop-up and
- submenus, however.
-
- This particular example is meant to show the mechanics of creating and
- handling events from submenus; in the interest of simplicity, certain nice
- features (like checkmarking the current font) have been omitted.
-
- Have fun.
-
- ------------------------------------------------------------------------------}
-
- PROGRAM PopMenus;
- {*
- * Pop-up Menu Example
- * Bryan Stearns 05May87
- *}
-
- USES MemTypes, Quickdraw, OSIntf, ToolIntf, PackIntf;
-
- {$R-} {no range checking}
- {$D+} {Generate debug symbols}
-
- CONST
- myDLOGid = 128; {our dialog template's resource ID}
- popMenuID = 128; {our menu's ID}
- myALRTid = 129; {our “need right machine & sys software” alert}
-
- {Items in our dialog box: there’s an OK button, and…}
- iPopUp = OK+1; {the Pop-up userItem}
- iPopPrompt = iPopUp+1; {the Prompt staticText}
- iDefOKRing = iPopPrompt+1; {the OK-button-default-ring userItem}
-
- {ASCII code for Return and Enter}
- crCode = 13; {(these are keyboard-independent)}
- enterCode = 3;
-
- {constants for positioning the default item within its box}
- leftSlop = 13; {leave this much space on left of title}
- rightSlop = 5; { this much on right}
- botSlop = 5; { this much below baseline}
-
- VAR myDialog: DialogPtr; {our dialog pointer}
- popUpBox: Rect; {boundsrect of our popUp's title box}
- promptBox: Rect; {boundsrect of its prompt}
- popMenu: MenuHandle; {our popUp's menu}
- hitItem: INTEGER; {result of ModalDialog}
- lastChoice: INTEGER; {the last-chosen item from the pop-up menu}
-
- theType: INTEGER; {used as temp in GetDItem/SetDItem}
- theHdl: Handle; {used as temp in GetDItem/SetDItem}
- theBox: Rect; {used as temp in GetDItem/SetDItem}
-
-
- {*
- * Draw our popUp’s current selection box
- * Note: This is called by the Dialog Manager (for
- * update events) as well as our own Filterproc.
- *}
- PROCEDURE DrawPopUp(theDialog: DialogPtr; theItem: INTEGER);
- VAR r: Rect;
- curFont: Str255;
- newWid, newLen, wid: INTEGER;
- BEGIN
- GetItem(popMenu,lastChoice,curFont); {get currently-selected item}
- r := popUpbox;
- WITH r DO BEGIN
- InsetRect(r,-1,-1); {make it a little bigger}
-
- {Make sure the title fits. Truncate it and add an ellipses (“…”)}
- {if it doesn’t (by the way, “…” is option-semicolon)}
- wid := (right - left) - (leftSlop + rightSlop); {available string area}
- newWid := StringWidth(curFont); {get current width}
- IF newWid > wid THEN BEGIN {doesn't fit - truncate it}
- newLen := LENGTH(curFont); {current length in characters}
- wid := wid - CharWidth('…'); {subtract width of ellipses}
-
- REPEAT {until it fits (or we run out of characters)}
- {drop the last character and its width}
- newWid := newWid - CharWidth(curFont[newLen]);
- newLen := PRED(newLen);
- UNTIL (newWid <= wid) OR (LENGTH(curFont) = 0);
-
- {add the ellipses character}
- newLen := SUCC(newLen); {one more char}
- curFont[newLen] := '…'; {it’s the ellipses}
- curFont[0] := CHR(newLen); {fix the length}
- END;
-
- {draw the box and its drop shadow}
- FrameRect(r);
- MoveTo(right,top+2); LineTo(right,bottom);
- LineTo(left+2,bottom);
-
- {draw the string}
- MoveTo(left+LeftSlop,bottom-BotSlop);
- DrawString(curFont);
- END;
- END; {DrawPopUp}
-
-
- {*
- * Draw the ring around the OK button, the way that
- * alert boxes get it. This procedure is only called
- * by the Dialog Manager for update events.
- *}
- PROCEDURE DrawOKDefault(theDialog: DialogPtr; theItem: INTEGER);
- VAR savePen: PenState;
- BEGIN
- GetPenState(savePen); {save the old pen state}
-
- GetDItem(theDialog, theItem, theType, theHdl, theBox); {get the item’s rect}
- PenSize(3,3); {make the pen fatter}
- FrameRoundRect(theBox,16,16); {draw the ring}
-
- SetPenState(savePen); {restore the pen state}
- END; {DrawOKDefault}
-
-
- {*
- * Filterproc for our dialog box
- * - supports Enter & Return --> OK.
- * - watches for userItem hits
- *}
- FUNCTION myFilter(theDialog: DialogPtr; VAR theEvent: EventRecord; VAR itemHit: INTEGER): BOOLEAN;
- VAR mouseLoc, popLoc: Point;
- newChoice: INTEGER;
- chosen,ignoreLong: LongInt;
- BEGIN
- itemHit := 0; {We return these two values. Initialize them.}
- myFilter := FALSE;
- SetPort(theDialog);
-
- WITH theEvent DO CASE what OF
- keyDown: BEGIN
- IF (theEvent.message MOD 256) IN [crCode, enterCode] THEN BEGIN
- {user pressed Return or Enter}
- GetDItem(theDialog, OK, theType, theHdl, theBox); {get the button's rect}
- HiliteControl(ControlHandle(theHdl), 1); {make it look...}
- Delay(3, ignoreLong); {...like the OK button was hit}
-
- myFilter := TRUE; {dialog is over}
- itemHit := OK; {have ModalDialog return that the user hit OK}
- END;
- END; {keydown case}
-
- mouseDown: BEGIN {"Click!"}
- mouseLoc := where; {copy the mouse position}
- GlobalToLocal(mouseLoc); {convert it to local coordinates}
-
- {Was the click in our item?}
- IF (FindDItem(theDialog, mouseLoc) + 1) = iPopUp THEN BEGIN {Yep, the click was ours!}
-
- {We're going to pop up our menu. Insert our menu into the menu list,}
- {then call CalcMenuSize (to work around a bug in the Menu Manager), }
- {then call PopUpMenuSelect and let the user drag around. Note that the}
- {(top,left) parameters to PopUpMenuSelect are our item’s, converted to}
- {global coordinates.}
- InvertRect(promptBox); {hilight the prompt}
- InsertMenu(popMenu,-1); {insert our menu in the menu list}
- popLoc := popUpBox.topLeft; {copy our item’s topleft}
- LocalToGlobal(popLoc); {convert back to global coords}
- CalcMenuSize(popMenu); {Work around Menu Mgr bug}
- WITH popLoc DO chosen := PopUpMenuSelect(popMenu, v, h, lastChoice);
- InvertRect(promptBox); {unhilight the prompt}
- DeleteMenu(popMenuID); {remove our menu from the menu list}
-
- {Was something chosen?}
- IF chosen <> 0 THEN BEGIN {yep, something was chosen}
- newChoice := LoWord(chosen); {get the chosen item number}
-
- IF newChoice <> lastChoice THEN BEGIN
- {the user chose an item other than the current one}
- SetItemMark(popMenu,lastChoice,' '); {unmark the old choice}
- SetItemMark(popMenu,newChoice,CHR(checkMark)); {mark the new choice}
- lastChoice := newChoice; {update the current choice}
-
- {Draw the new title}
- EraseRect(popUpBox);
- DrawPopUp(theDialog,iPopUp);
-
- myFilter := TRUE; {dialog is over}
- itemHit := iPopUp; {have ModalDialog return that the user changed items}
- END; {if this choice was not the current choice}
- END; {if something was chosen}
-
- END; {if clicked in our userItem}
- END; {mousedown case}
- END {case}
- END; {myFilter}
-
-
- {*
- * Main
- *}
- BEGIN
- {Initialize all the usual managers}
- InitGraf(@thePort);
- InitFonts;
- FlushEvents(everyEvent,0);
- InitWindows;
- InitMenus;
- TEInit;
- InitDialogs(NIL);
- InitCursor;
-
- {Check to make sure we’re running on a machine}
- {capable of supporting pop-up menus}
- (** SysEnvirons(xxx); **)
- IF FALSE THEN BEGIN {Sorry, see your dealer}
- hitItem := StopAlert(myALRTid,NIL); {put up the alert}
- ExitToShell; {back to the finder}
- END;
-
- {Get a menu containing the current set of fonts}
- popMenu := NewMenu(popMenuID,'notUsed'); {Create a menu (its title is ignored)}
- AddResMenu(popMenu,'FONT'); {fill it with fonts}
- lastChoice := 1; {make the first item the default}
- SetItemMark(popMenu,1,CHR(checkMark)); {check it}
-
- {Get our dialog box, and set up our useritem (the dialog template}
- {defines this dialog to be hidden, so that it won’t be drawn until}
- {we’ve installed our userItem’s drawing procedure)}
- myDialog := GetNewDialog(myDLOGid,NIL,POINTER(-1));
-
- {Find out where our popUp's rectangle is, and set its item handle to be}
- {a pointer to our popup-drawing procedure}
- GetDItem(myDialog,iPopUp,theType,theHdl,popUpbox); {get the rect}
- SetDItem(myDialog,iPopUp,theType,@DrawPopUp,popUpbox); {set the procPtr}
-
- {Find out where the prompt for our popUp is, so that we can invert its}
- {rect when popping up our menu}
- GetDItem(myDialog,iPopPrompt,theType,theHdl,promptBox); {get the rect}
-
- {Move our default-OK-button userItem to around the OK button, and set its}
- {item handle to be a pointer to our other drawing procedure}
- GetDItem(myDialog,OK,theType,theHdl,theBox); {get the OK button's rect}
- InsetRect(theBox,-4,-4); {make the rect a little bigger}
- {set the same old type, our procptr, and the new box}
- SetDItem(myDialog,iDefOKRing,userItem+itemDisable,@DrawOKDefault,theBox);
-
- ShowWindow(myDialog); {show it, finally!}
-
- REPEAT
-
- ModalDialog(@myFilter,hitItem);
-
- CASE hitItem OF {which item was hit, if any?}
-
- iPopUp: BEGIN {he chose a new item in our pop-up}
- {Do whatever needs to be done when the choice changes; }
- {the on-screen current value box has already been updated.}
- END; {iPopUp case}
-
- {other items here}
-
- END; {case}
-
- UNTIL hitItem = OK;
-
- HideWindow(myDialog); {hide the window, as feedback}
- END.
-
-
-